/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package LOF;

import java.text.DecimalFormat;
import java.util.Arrays;
import weka.core.EuclideanDistance;
import weka.core.Instance;
import weka.core.Instances;

/**
 *
 * @author Agnitto
 */
public class LetUsBegin {

    Instances allpts;
    double[][] allptsdist;
    double[][][] allptsclosepts;
    double[] sorted;
    double[] lrd;
    double[] lof;
    double[] maxlof;
    double summing;
    int[] lofs;
    int minptsUB;
    int minptsLB;
    int size;
    EuclideanDistance euc;
    DecimalFormat dcm = new DecimalFormat("##.##");

    /**
     * Constructor that sets up and performs LOF evaluation
     * 
     * @param inst
     * @param minptsLB the lower min-pt value (must be > 10)
     * @param minptsUB the upper min-pt value (just smaller than cluster size)
     */
    public LetUsBegin(Instances inst, int minptsLB, int minptsUB) {
        allpts = inst;
        size = allpts.size();
        euc = new EuclideanDistance(allpts);
        this.minptsUB = minptsUB;
        this.minptsLB = minptsLB;
        allptsdist = new double[size][size];
        allptsclosepts = new double[size][this.minptsUB][2];
        maxlof = new double[size];


        startForAll();
        setClosest();

        // run through range of minPts evaluations
        for (int minptslb = minptsLB; minptslb < (this.minptsUB + 1); minptslb++) {
            localReachDensity(minptsLB);
            outlierFactor(minptsLB);
            getMax();
        }

    }

    /**
     * calculates all distances between pts.
     */
    private void startForAll() {
        for (int i = 0; i < size; i++) {
            for (int j = 0; j < size; j++) {
                if (i != j) {
                    allptsdist[i][j] = distance(allpts.get(i), allpts.get(j));
                }
            }
        }
    }

    /**
     * Sorts the closest points (minptsUB) to each points
     */
    private void setClosest() {
        int index;
        for (int i = 0; i < size; i++) {
            sorted = new double[size - 1];
            index = 0;
            for (int j = 0; j < size; j++) {
                if (allptsdist[i][j] != 0.0) {
                    sorted[index] = allptsdist[i][j];
                    index++;
                }
            }

            Arrays.sort(sorted);

            for (int k = 0; k < minptsUB; k++) {
                for (int l = 0; l < size; l++) {
                    if (l != i && sorted[k] == allptsdist[i][l]) {
                        allptsclosepts[i][k][1] = sorted[k];
                        allptsclosepts[i][k][0] = l;
                    }
                }
            }

        }
    }

    /**
     * Calculates local reach density for current minpts evaluation
     * 
     * @param currminpt 
     */
    private void localReachDensity(int currminpt) {
        lrd = new double[size];
        for (int i = 0; i < size; i++) {
            lrd[i] = 1 / (allptsclosepts[i][currminpt - 1][1]);
        }
    }

    /**
     * Calculates outlier factors for current minpts evaluation
     * 
     * @param currminpt 
     */
    private void outlierFactor(int currminpt) {
        lof = new double[size];
        for (int i = 0; i < size; i++) {
            summing = 0.0;
            for (int j = 0; j < currminpt; j++) {
                summing += (lrd[(int) allptsclosepts[i][j][0]]) / lrd[i];
            }
            lof[i] = summing / currminpt;
        }

    }

    /**
     * Sets max LOF value through range of minpts evalution
     */
    private void getMax() {
        for (int i = 0; i < size; i++) {
            if (lof[i] > maxlof[i]) {
                maxlof[i] = lof[i];
            }
        }
    }

    /**
     * Gets all outliers (where LOF > 2)
     * 
     * @return array of outlier indexes
     */
    public int[] getOutliers() {

        // find number of outliers
        int num = 0;
        for (int i = 0; i < size; i++) {
            if (maxlof[i] >= 2) {
                num++;
            }
        }

        int[] outliers = new int[num];

        //fill array with outliers
        num = 0;
        for (int i = 0; i < size; i++) {
            if (maxlof[i] >= 2) {
                outliers[num] = i;
                num++;
            }
        }
        return outliers;
    }

    /**
     * Gets distance between 2 points
     * 
     * @param pt
     * @param pt2
     * @return distance
     */
    private double distance(Instance pt1, Instance pt2) {
        return euc.distance(pt1, pt2);
    }
}











